# Game Object Types (all integers)
# 0 - Ball:  During think it moves according to its past dynamics (this will bounce off walls and continue when shot by players)
# 1 - Bullet: This comes out of an 'agent' when it shoots.  During think it continues to move in the direction it was shot.
# (2+team_num) - Agent: This is the main class of the game.  During think it does the sense/think/act cycle to interact with the world.  Team 0 would be object number 2.
import game_model, ga_agent_factory,ga_agent,threading # TODO Extend from this class again and fix the error
import sensor_range_finder, sensor_pie_slices, sensor_radar, game_object, ball_object, bullet_object, wall, sensor_agent_state, sensor_goal, sensor_ball, scripted_object,sensor_nclosest_radar
import math
from numpy import *
from numpy.linalg import *
from math import *
import time
pi=3.1415926535897931
from copy import copy
class soccer_game_model(object):
	def __init__(self,world_model,wall_graphics,agent_graphics,text_graphics):
		self.wm=world_model
		self.w_graphics=wall_graphics
		self.a_graphics=agent_graphics
		self.t_graphics=text_graphics
		
		# Team Properties
		self.num_teams=2
		self.num_agents_per_team=1
	
		self.start_positions=[[[460,300],[460,420],[420,360],[500,240],[500,480]],[[260,420],[260,300],[300,360],[220,240],[220,480]]]
		self.start_headings=[pi,0]
		self.team_image=['yellowagent','blueagent']
		self.goal_pos=[[90,360],[625,360]]
		self.respawn_points=[[360,150],[360,567]]
		self.respawn_headings=[pi/2, -pi/2]
		self.ball_image = 'ball'
		self.team_text_pos = [[620,620],[100,100]]
		self.timer_text_pos = [620,100]
		self.final_text_pos = [360,100]
		self.score_text = 'Score: '
		
		# GA Factory Instantiation
		self.ga_fac=[None]*self.num_teams
		self.ga_format=[[4,3,3],[3,3,3],[2,3,3],[2,3,3]]
		self.fit_weights_lock=threading.Lock()
		self.fit_weights=[[1,50,25,25,1],[1,50,25,25,1]] # TODO hook this up to the GUI
		for team_iter in range(self.num_teams):
			self.ga_fac[team_iter]=ga_agent_factory.ga_agent_factory(self,self.ga_format,team_iter)
			
		# Agent Actions
		self.agent_actions={'move_rel_world':self.wm.object_move_rel_world,'move_rel_object':self.wm.object_move_rel_object,'turn_rel':self.wm.object_turn_rel, 'shoot':self.agent_shoot}
		
		# World Properties
		self.walls=[(0,300,0,420),(0,420,120,420),(120,420,300,600),(300,600,420,600),(420,600,600,420),(600,420,720,420),(720,420,720,300),(720,300,600,300),(600,300,420,120),(420,120,300,120),(300,120,120,300),(120,300,0,300)]
		self.wm.set_callbacks(self.obj_collision_callback,self.wall_collision_callback)
		self.goals=[(0,300,120,420), (600,300,720,420)]
		
		# Game state
		self.game_length=180.0
		self.start_game_time=0
		self.team_scores={0:0,1:0}
		self.game_active=True
		self.bullet_num=11
		self.graphics_mode='full'
		self.respawn_time = 3.0 #The time, in seconds, that an agent spends dead before it respawns
		self.shooting_interval = 5.0 #The time, in seconds, that an agent must wait before it can shoot again
		self.temp_bullets={}
		self.remove_list={}
		self.dead_agents={}
		self.objective_weights=[]  # This is a lists (one for each team) of lists (one for each objective function).  Each player modifies the weights to get their "smart" agents to do what they want (as opposed to scripted agents).
		self.ball_holder=-1 # If this is -1, neither team has the ball, if it is >=0, then the number is the game object that controls it.  The team that is controlling it can be retrieved by checking the object_type of the agent that has it.
		self.last_holder=-1
		
		# Training Hacks
		self.force_player_to_have_ball=False
		self.force_ball_to_player=2
		self.hide_player=False
		self.players_to_hide=[]
		self.hide_location=[0,0]
		self.reset_timer=True
		
	def save_ga(self):
		for team_iter in range(self.num_teams):
			self.ga_fac[team_iter].save_state()
	def load_ga(self):
		for team_iter in range(self.num_teams):
			self.ga_fac[team_iter].load_state('team'+str(team_iter)+'.brains')
		
	def force_ball_to_player_hack(self):
		if self.force_player_to_have_ball:
			self.take_ball(self.force_ball_to_player)
			self.fit_sbm_new_holder_agent(self.force_ball_to_player)
	def force_players_to_hide_hack(self):
		if self.hide_player:
			for i in self.players_to_hide:
				self.wm.objects[i].pos=copy(self.hide_location)
	def force_timer_reset_hack(self):
		if self.reset_timer:
			self.start_game_time = time.time()
	def set_team_colors(self, team_colors):
		self.team_image = team_colors
	def wall_collision_callback(self,colliding_object,collided_walls,disp=None):
		"""Returns the object that that may be colliding with the wall, a list of walls it collided with, and the closest distance to the walls.  Note that this is called whether or not a wall is hit!"""
		# Remove bullets that hit walls, slow down their positions 
		if self.wm.objects[colliding_object].object_type == 1:
			self.wm.objects[colliding_object].max_velocity = max(min(disp,self.wm.objects[colliding_object].max_velocity),self.wm.min_wall_dist)
			if len(collided_walls) > 0:
				self.remove_list[colliding_object]='kill'
				
		# Change the angle of a ball bouncing off of a wall to create a realistic reflection
		if self.wm.objects[colliding_object].object_type == 0:
			self.wm.objects[colliding_object].max_velocity = max(min(disp,self.wm.objects[colliding_object].max_velocity),self.wm.min_wall_dist)
			if len(collided_walls) > 0:
				self.wm.objects[colliding_object].max_velocity = 10
				xdiff=self.wm.walls[collided_walls[0]].points[0] - self.wm.walls[collided_walls[0]].points[2]
				ydiff=self.wm.walls[collided_walls[0]].points[1] - self.wm.walls[collided_walls[0]].points[3]
				wall_heading= atan2(ydiff,xdiff)
				self.wm.objects[colliding_object].heading = wall_heading + (wall_heading - self.wm.objects[colliding_object].heading) 
	def obj_collision_callback(self,colliding_object,collided_object):
		"""Handles bullets hitting, agents colliding, and balls hitting walls/bullets/agents"""
		# Handle collision events between agents
		if self.wm.objects[colliding_object].object_type > 1 and self.wm.objects[collided_object].object_type > 1:
			pos0 = array(self.wm.objects[collided_object].pos)
			pos1 = array(self.wm.objects[colliding_object].pos)
			disp = pos0-pos1
			heading=atan2(disp[1],disp[0])
			eps=.01
			obj_dist=norm(disp)
			mag=self.wm.objects[collided_object].radius+self.wm.objects[colliding_object].radius+eps-obj_dist
			new_disp=[cos(heading)*mag,sin(heading)*mag]
			#print('CollisionCall - Colliding:%d Collided: %d NewDisp: %s NewMag: %f NewDist:%f') %(colliding_object,collided_object,str(new_disp),mag,obj_dist)
			#self.wm.object_move_rel_world(collided_object,new_disp)
			
			if collided_object == self.ball_holder:
				self.steal_ball(colliding_object, collided_object)
				
			elif colliding_object == self.ball_holder:
				self.steal_ball(collided_object, colliding_object)
		
		# Handle collision events between agents and the ball
		if self.wm.objects[colliding_object].object_type > 1 and self.wm.objects[collided_object].object_type == 0:
			self.take_ball(colliding_object)
			
		if self.wm.objects[colliding_object].object_type == 0 and self.wm.objects[collided_object].object_type > 1:
			self.take_ball(collided_object)
			
		#Handle collision events between bullets and agents
		if self.wm.objects[colliding_object].object_type == 1:
			self.hits(colliding_object, collided_object)
		elif self.wm.objects[collided_object].object_type == 1:
			self.hits(collided_object, colliding_object)
					
	def hits(self, bullet_num, agent_num):
		'''Removes the bullet from the game, determines whether the bullet kills the agent, and if so, kills the agent.'''
		killer = self.wm.objects[bullet_num].creator
		self.remove_list[bullet_num]='kill'
		if self.wm.objects[agent_num].object_type > 1:
			if self.wm.objects[agent_num].object_type-2 != self.wm.objects[bullet_num].team:
				self.wm.objects[killer].fitness_vector[1] += 1
				self.dead_agents[agent_num]='kill'
	
	def steal_ball(self, agent_1, agent_2):
		'''Results in agent_2 losing possession of the ball and agent_1 gaining possession of the ball.'''		
		self.wm.objects[agent_2].final_dist, angle = self.wm.objects[agent_2].senses['goal']()
		self.wm.objects[agent_2].fitness_vector[0] += self.wm.objects[agent_2].first_dist - self.wm.objects[agent_2].final_dist
		self.wm.objects[agent_2].graphics_state.pop(-1)
		
		if self.wm.objects[agent_2].object_type != self.wm.objects[agent_1].object_type:
			self.wm.objects[agent_2].fitness_vector[2] -= 1
			self.wm.objects[agent_1].fitness_vector[3] += 1
		
		self.ball_holder = agent_1
		self.last_holder = agent_1
		self.wm.objects[agent_1].first_dist, angle = self.wm.objects[agent_1].senses['goal']()
		self.wm.objects[agent_1].graphics_state.append(1)

	def fit_sbm_new_holder_agent(self,agent_num):
		"""This can be called any time to update the agents fitness while it has the ball, it does nothing if called with an agent that doesn't have the ball"""
		print('AgNum:%d LastHold:%d')%(agent_num,self.last_holder)
		if self.last_holder != agent_num:
			return
		self.wm.objects[self.last_holder].first_dist, angle = self.wm.objects[self.last_holder].senses['goal']()
	def fit_sbm_new_holder(self,agent_num):
		"""This is called right before the ball is caught, thus we can use the ball's sensors"""
		self.last_holder = agent_num
		if agent_num <= self.num_agents_per_team:
		       	self.wm.objects[agent_num].first_dist, angle = self.wm.objects[0].senses['goal_0']()
		else:
		       	self.wm.objects[agent_num].first_dist, angle = self.wm.objects[0].senses['goal_1']()
	def fit_sbm_update_fitness_ball(self,agent_num):
		"""This is called right before the ball is caught, thus we can still use the ball's sensors"""
		if self.last_holder <= self.num_agents_per_team:
		       	self.wm.objects[self.last_holder].final_dist, angle = self.wm.objects[0].senses['goal_0']()
		else:
		       	self.wm.objects[self.last_holder].final_dist, angle = self.wm.objects[0].senses['goal_1']()
		self.wm.objects[self.last_holder].fitness_vector[0] += self.wm.objects[self.last_holder].first_dist - self.wm.objects[self.last_holder].final_dist
	def fit_sbm_update_fitness_agent(self,agent_num):
		"""This can be called any time to update the agents fitness while it has the ball, it does nothing if called with an agent that doesn't have the ball"""
		if self.last_holder != agent_num:
			return
		self.wm.objects[self.last_holder].final_dist, angle = self.wm.objects[self.last_holder].senses['goal']()
		self.wm.objects[self.last_holder].fitness_vector[0] += self.wm.objects[self.last_holder].first_dist - self.wm.objects[self.last_holder].final_dist

	def take_ball(self, agent_num):
		'''Results in agent_num taking the ball'''
		self.ball_holder = agent_num
		if self.last_holder == -1:
			self.fit_sbm_new_holder(agent_num)
		elif agent_num != self.last_holder:
			if self.wm.objects[self.last_holder].object_type != self.wm.objects[agent_num].object_type:
				self.wm.objects[self.last_holder].fitness_vector[2] -= 1
				self.wm.objects[agent_num].fitness_vector[3] += 1
			self.fit_sbm_update_fitness_ball(agent_num)
			self.fit_sbm_new_holder(agent_num)
			
		self.wm.objects[agent_num].graphics_state.append(1)
		self.wm.objects[0].pos = [-20,-20]
		self.wm.objects[0].thrown = False
		
	def think(self):
		#Check the timer's status to see whether or not the game should end
		self.force_timer_reset_hack()
		cur_time = time.time()
		if cur_time >= self.start_game_time + self.game_length:
			self.game_active = False
			
		if self.game_active:
			self.t_graphics.text_draw(2,self.timer_text_pos,self.time_str())
			for cur_obj in self.wm.objects.itervalues():
				if not self.remove_list.has_key(cur_obj.object_num):
					cur_ball_mag_orig=0
					cur_state_orig=0
					if cur_obj.object_type>=2:
						cur_ball_mag_orig,cur_ball_angle=cur_obj.senses['ball']()
						cur_state_orig=cur_obj.senses['state']()
					# Let each object do its thing and then draw the result
					dont_draw=cur_obj.think()  # Give the game object time to think
					if dont_draw != True:  # Basic draw optimization, if anything but true is returned, draw
						cur_obj.draw()   # (Re)draw this object
					if cur_obj.object_type>=2:
						cur_ball_mag_new,cur_ball_angle=cur_obj.senses['ball']()
						cur_state_new=cur_obj.senses['state']()
					# The states are 0='I have the ball', 1='Someone on my team has the ball', 2='Enemy has the ball', 3=Loose Ball
						if cur_state_orig==cur_state_new: # If we changed states, then don't change the fitness
							if cur_state_new==2 or cur_state_new==3:
								cur_obj.fitness_vector[4]+=cur_ball_mag_orig-cur_ball_mag_new
					# Check to see if an agent scored a goal 
					if self.ball_holder != -1:
						ball_pos = self.wm.objects[self.ball_holder].pos
						team_num = self.wm.objects[self.ball_holder].object_type - 2
						if self.goals[team_num][0]<ball_pos[0]<self.goals[team_num][2] and self.goals[team_num][1]<ball_pos[1]<self.goals[team_num][3]:
							
							self.wm.objects[self.ball_holder].fitness_vector[0] = self.wm.objects[self.ball_holder].first_dist
							
							self.team_scores[team_num] += 1
							self.t_graphics.text_draw(team_num,self.team_text_pos[team_num],self.score_text+str(self.team_scores[team_num]))
							self.ball_holder=-1
							self.last_holder=-1
							self.reset_object_locations()
							break
							
			# Create new objects (bullets)
			bullets = []
			for temp_bullet in self.temp_bullets:
				self.wm.new_object(temp_bullet, self.temp_bullets[temp_bullet])
				bullets.append(temp_bullet)
				collided_objects = self.wm.overlapping_objects(temp_bullet)
				for obj in collided_objects:
					self.obj_collision_callback(temp_bullet,obj)
			for bullet in bullets:
				del self.temp_bullets[bullet]
			self.temp_bullets = {}
					
			# Resolve destroyed objects
			for obj_num in self.remove_list:
				self.wm.remove_object(obj_num)
			self.remove_list = {}
			
			# Resolve dead agents
			for obj_num in self.dead_agents:
				if self.ball_holder == obj_num: # Person that died had the ball, put the ball where they died
					self.wm.objects[obj_num].final_dist, angle = self.wm.objects[obj_num].senses['goal']()
					self.wm.objects[obj_num].fitness_vector[0] = self.wm.objects[obj_num].first_dist - self.wm.objects[obj_num].final_dist
					print self.wm.objects[obj_num].fitness_vector[0]
					self.ball_holder = -1
					self.last_holder = -1
					self.wm.objects[0].pos = copy(self.wm.objects[obj_num].pos)
				self.wm.objects[obj_num].is_dead = True
				self.wm.objects[obj_num].time_of_death = time.time()
				self.wm.objects[obj_num].pos = [-50,-50]
				self.wm.objects[obj_num].graphics_state=[0]
				self.wm.objects[obj_num].draw()
				if isinstance(self.wm.objects[obj_num],ga_agent.ga_agent): # If it is a GA agent, tell it it can go ahead and swap brains if it wants
					self.ga_fac[self.wm.objects[obj_num].object_type-2].refresh_agent(self.wm.objects[obj_num])
			self.dead_agents = {}
			self.respawn_agents()
			
		else:
			self.clear_world()
			if self.team_scores[0] > self.team_scores[1]:
				self.t_graphics.text_draw(3,self.final_text_pos,'Team 1 wins!')
			elif self.team_scores[0] < self.team_scores[1]:
				self.t_graphics.text_draw(3,self.final_text_pos,'Team 2 wins!')
			else:
				self.t_graphics.text_draw(3,self.final_text_pos,'Tied game!')
	def respawn_agents(self):
		'''Respawns any dead agents who have met or exceeded the necessary respawn time, as long as one of the two respawn points is cleared.'''
		for obj in self.wm.objects:
			cur_time = time.time()
			if self.wm.objects[obj].is_dead and cur_time >= self.wm.objects[obj].time_of_death + self.respawn_time:
				pos_index = 0
		
				pos = copy(self.respawn_points[pos_index])
				self.wm.objects[obj].pos = pos
				collided_objects = self.wm.overlapping_objects(obj)
				pos_index += 1
				
				while len(collided_objects) > 0 and pos_index < len(self.respawn_points):	
					pos = copy(self.respawn_points[pos_index])
					self.wm.objects[obj].pos = pos
					collided_objects = self.wm.overlapping_objects(obj)
					pos_index += 1
					
				if len(collided_objects) <= 0:
					pos_index -= 1
				if pos_index >= len(self.respawn_points):
					self.wm.objects[obj].pos = [-50,-50]
					continue
					
				heading = copy(self.respawn_headings[pos_index])
				self.wm.objects[obj].heading = heading
				self.wm.objects[obj].is_dead = False
		
	def agent_state(self,object_num):
		"""Provides the state of the ball relative to an agent"""
		# The states are 0='I have the ball', 1='Someone on my team has the ball', 2='Enemy has the ball', 3=Loose Ball
		if self.ball_holder==object_num:
			return 0
		if self.ball_holder==-1:
			return 3
		if self.wm.objects[object_num].object_type==self.wm.objects[self.ball_holder].object_type:
			return 1
		else:
			return 2
		return
	
	def agent_shoot(self,object_num):
		"""Ability that agents have that will shoot a bullet if they can at the moment, or throw the ball if they are holding it"""
		cur_time = time.time()
		if cur_time >= self.wm.objects[object_num].last_shot + self.shooting_interval:
			self.wm.objects[object_num].last_shot = time.time()
			heading = copy(self.wm.objects[object_num].heading)
			pos = copy(self.wm.objects[object_num].pos)
			if object_num == self.ball_holder:
				total_radius =  self.wm.objects[object_num].radius + 10
			else:
				total_radius =  self.wm.objects[object_num].radius + 5
			
			pos[0] += (total_radius+2)*math.cos(heading)
			pos[1] += (total_radius+2)*math.sin(heading)
			
			min_dist, ignore_walls = self.wm.min_wall_collision_dist(object_num, heading)
			if min_dist > total_radius:
				if object_num == self.ball_holder:
					self.ball_holder = -1
					self.wm.objects[object_num].graphics_state.pop(-1)
					self.wm.objects[0].heading = heading
					self.wm.objects[0].pos = pos
					self.wm.objects[0].thrown = True
					collided_objects = self.wm.overlapping_objects(0)
					for obj in collided_objects:
						self.obj_collision_callback(0,obj)
				else:
					actions = {'move_rel_object':self.wm.object_move_rel_object}
					self.temp_bullets[self.bullet_num] = bullet_object.bullet_object(actions,None,1,self.bullet_num,20,0,5,self.a_graphics.agent_graphics_draw,self.a_graphics.agent_graphics_die,pos,heading,'bullet',self.wm.objects[object_num].object_type-2,object_num)
					self.bullet_num += 1
			

	def create_game_world(self):
		'''Creates all walls, spawns all agents in their starting locations, and creates the ball at the beginning of the game, as well as initializing game values.'''
		# Create walls
		for i in xrange(len(self.walls)):
			w=wall.wall(self.walls[i],self.w_graphics.wall_draw)
			self.wm.new_wall(i,w)
			
		# Create agents
		for team in xrange(2):
			for agent_num in xrange(self.num_agents_per_team):
				i=1+agent_num+self.num_agents_per_team*team
				# Agent Sensors
				range_finder=sensor_range_finder.sensor_range_finder(i, self.wm, [-15*pi/180, 0, 15*pi/180], 200)
				pie_slices=sensor_pie_slices.sensor_pie_slices(i,self.wm,[-3*pi/4,-pi/4,pi/4,3*pi/4], 100)
				radar=sensor_radar.sensor_radar(i, self.wm, 100)
				friend_radar=sensor_nclosest_radar.sensor_nclosest_radar(i, self.wm, 1,120,2)
				enemy_radar=sensor_nclosest_radar.sensor_nclosest_radar(i, self.wm, 1,120,1)
				state_sensor=sensor_agent_state.sensor_agent_state(i, self)
				ball_sensor=sensor_ball.sensor_ball(i,self.wm,self)
				goal_sensor=sensor_goal.sensor_goal(i,self.wm,self.goal_pos[team])
				agent_sensors={'enemy_radar':enemy_radar.sense,'friend_radar':friend_radar.sense,'range_finder':range_finder.sense, 'radar':radar.sense, 'pie_slices':pie_slices.sense, 'state':state_sensor.sense,'ball':ball_sensor.sense, 'goal':goal_sensor.sense}
				pos = copy(self.start_positions[team][agent_num])
				heading = copy(self.start_headings[team])
				#Determine what type of agents to create
				if team==4:
					if agent_num==0:
						temp_agent=game_object.game_object(self.agent_actions,agent_sensors,2+team,i,20,pi/16,22,self.a_graphics.agent_graphics_draw,self.a_graphics.agent_graphics_die,pos,heading,self.team_image[team])
					else:
						temp_agent=self.ga_fac[team].create_agent_slot(self.agent_actions,agent_sensors,2+team,i,10,pi/16,22,self.a_graphics.agent_graphics_draw,self.a_graphics.agent_graphics_die,copy(pos),heading,self.team_image[team])
				elif team==1 or team==0:
					temp_agent=self.ga_fac[team].create_agent_slot(self.agent_actions,agent_sensors,2+team,i,10,pi/16,22,self.a_graphics.agent_graphics_draw,self.a_graphics.agent_graphics_die,copy(pos),heading,self.team_image[team])
				else:
					temp_agent=scripted_object.scripted_object(self.agent_actions,agent_sensors,2+team,i,10,pi/16,22,self.a_graphics.agent_graphics_draw,self.a_graphics.agent_graphics_die,pos,heading,self.team_image[team])
				self.wm.new_object(i,temp_agent)
			#Create the text to display the score	
			self.t_graphics.text_draw(team,self.team_text_pos[team],self.score_text+str(self.team_scores[team]))
				
		# Create ball
		state_sensor=sensor_agent_state.sensor_agent_state(0, self)
		goal_sensor_0=sensor_goal.sensor_goal(0,self.wm,self.goal_pos[0])
		goal_sensor_1=sensor_goal.sensor_goal(0,self.wm,self.goal_pos[1])
		ball_sensors={'state':state_sensor.sense, 'goal_0':goal_sensor_0.sense, 'goal_1':goal_sensor_1.sense}
		ball=ball_object.ball_object(self.agent_actions,ball_sensors,0,0,10,0,10,self.a_graphics.agent_graphics_draw,self.a_graphics.agent_graphics_die,[360,360],0,self.ball_image)
		self.wm.new_object(0,ball)
		#Create text to display the remaining time
		self.start_game_time=time.time()
		self.t_graphics.text_draw(2,self.timer_text_pos,self.time_str())
		
		return
	def time_str(self):
		'''Returns a string representation of the time left in the game.'''
		cur_time = time.time()
		minutestr = str(int(floor((self.game_length-(cur_time - self.start_game_time))/60)))
		seconds = int((self.game_length-(cur_time - self.start_game_time))%60)
		if seconds < 10:
			secondstr = '0' + str(seconds)
		else:
			secondstr = str(seconds)
		return minutestr + ':' + secondstr
	def clear_world(self):
		'''Eliminates all the objects in the world'''
		# Delete walls
		walls = []
		for wall in self.wm.walls:
			walls.append(wall)
		for wall in walls:
			self.wm.remove_wall(wall)
			
		#Delete objects
		objs = []
		for obj in self.wm.objects:
			objs.append(obj)
		for obj in objs:
			self.wm.remove_object(obj)
	def reset_object_locations(self):
		'''Places the ball and agents back in their initial positions at the beginning of a new round, and eliminates all extra objects (bullets) from the world.'''
		#Clear the dead and remove lists to avoid issues occuring when the next round begins.
		self.remove_list = {}
		self.dead_agents = {}
		
		# Iterate through the objects in the world, deleting any stray bullets and setting all agents' is_dead to False
		bullets = []
		for cur_obj in self.wm.objects.itervalues():
			cur_obj.is_dead = False
			if cur_obj.object_num > self.num_agents_per_team*2:
				bullets.append(cur_obj.object_num)
		for bullet in bullets:
			self.wm.remove_object(bullet)
		
		#Reset the ball's position
		self.wm.objects[0].pos = [360,360]
		
		#Iterate through both teams' agents to reset their positions and headings
		for team in xrange(2):
			for agent_num in xrange(self.num_agents_per_team):
				i=1+agent_num+self.num_agents_per_team*team
				pos = copy(self.start_positions[team][agent_num])
				heading = copy(self.start_headings[team])
				self.wm.objects[i].pos = pos
				self.wm.objects[i].heading = heading
				self.wm.objects[i].graphics_state=[0]
		self.force_players_to_hide_hack()
		self.force_ball_to_player_hack()
		
	def switch_graphics(self):
		'''Changes the graphics to display circles for debug mode, or reverts to full graphics mode.'''
		if self.graphics_mode == 'full':
			self.set_debug_graphics()
		else:
			self.set_full_graphics()
	def set_debug_graphics(self):
		for i in self.wm.objects:
			if i < 11:
				self.wm.objects[i].die()
				if i == 0:
					self.wm.objects[i].im_file = 'test_ball'
				elif i < self.num_agents_per_team + 1:
					self.wm.objects[i].im_file = 'test_agent_1'
				else:
					self.wm.objects[i].im_file = 'test_agent_2'
		self.graphics_mode='debug'
	def set_full_graphics(self):
		for i in self.wm.objects:
			if i < 11:
				self.wm.objects[i].die()
				if i == 0:
					self.wm.objects[i].im_file = self.ball_image
				elif i < self.num_agents_per_team + 1:
					self.wm.objects[i].im_file = self.team_image[0]
				else:
					self.wm.objects[i].im_file = self.team_image[1]
		self.graphics_mode='full'
	def quit(self):
		for team_iter in range(self.num_teams):
			self.ga_fac[team_iter].stop_maintenance_thread()
		
